/* utmplib.c - 从 utmp 文件缓冲读取的函数库  
 *  
 *      函数包括  
 *              utmp_open(filename)   - 打开文件  
 *                      出错时返回 -1  
 *              utmp_next()          - 返回指向下一个结构的指针  
 *                      到达文件末尾时返回 NULL  
 *              utmp_close()         - 关闭文件  
 *  
 *      每次读取 NRECS 条记录，然后从缓冲区中分配记录  
 */  
#include <stdio.h>  
#include <stdlib.h>     /* 添加 stdlib.h 以使用 exit() */  
#include <fcntl.h>  
#include <sys/types.h>  
#include <unistd.h>     /* 添加 unistd.h 以使用 close() */  
#include <utmp.h>  

/* 宏定义 */  
#define NRECS   16  
#define NULLUT  ((struct utmp *)NULL)  
#define UTSIZE  (sizeof(struct utmp))  

/* 静态变量 */  
static char utmpbuf[NRECS * UTSIZE]; /* 存储缓冲区 */  
static int num_recs = 0;             /* 存储的记录数 */  
static int cur_rec = 0;              /* 下一个要提供的记录的索引 */  
static int fd_utmp = -1;             /* 文件描述符，初始为 -1 表示未打开 */  

/* 函数声明 */  
int utmp_open(char *filename);  
struct utmp *utmp_next(void);  
void utmp_close(void);  
int utmp_reload(void);  

/**  
 * utmp_open - 以只读方式打开 utmp 文件  
 * @filename: 要打开的文件名  
 *  
 * 返回:  
 *      打开的文件描述符，出错时返回 -1  
 */  
int utmp_open(char *filename)  
{  
    fd_utmp = open(filename, O_RDONLY); /* 打开文件 */  
    if (fd_utmp == -1) {  
        perror("utmp_open: open");      /* 打开文件失败，打印错误信息 */  
        return -1;  
    }  
    cur_rec = num_recs = 0;             /* 初始化记录索引和记录数 */  
    return fd_utmp;                      /* 返回文件描述符 */  
}  

/**  
 * utmp_next - 获取下一个 utmp 记录  
 *  
 * 返回:  
 *      指向下一个 utmp 结构的指针，若到达文件末尾或出错则返回 NULL  
 */  
struct utmp *utmp_next(void)  
{  
    struct utmp *recp;  

    if (fd_utmp == -1)                   /* 检查文件是否已打开 */  
        return NULLUT;  

    /* 如果当前缓冲区中的记录已全部提供，并尝试重新加载缓冲区 */  
    if (cur_rec == num_recs && utmp_reload() == 0)  
        return NULLUT;  

    /* 获取下一个记录的地址 */  
    recp = (struct utmp *)&utmpbuf[cur_rec * UTSIZE];  
    cur_rec++;  
    return recp;  
}  

/**  
 * utmp_reload - 重新加载缓冲区中的记录  
 *  
 * 读取下一个批次的记录到缓冲区中  
 *  
 * 返回:  
 *      读取到的记录数，若读取失败则返回 0  
 */  
int utmp_reload(void)  
{  
    ssize_t amt_read;  

    /* 从 utmp 文件中读取记录到缓冲区 */  
    amt_read = read(fd_utmp, utmpbuf, NRECS * UTSIZE);  
    if (amt_read < 0) {                   /* 读取失败 */  
        perror("utmp_reload: read");  
        return 0;  
    }  

    num_recs = amt_read / UTSIZE;          /* 计算读取到的记录数 */  
    cur_rec = 0;                            /* 重置当前记录索引 */  
    return num_recs;  
}  

/**  
 * utmp_close - 关闭 utmp 文件  
 *  
 * 如果文件已打开，则关闭之  
 */  
void utmp_close(void)  
{  
    if (fd_utmp != -1) {                    /* 检查文件是否已打开 */  
        if (close(fd_utmp) == -1) {  
            perror("utmp_close: close");   /* 关闭文件失败，打印错误信息 */  
        }  
        fd_utmp = -1;                        /* 重置文件描述符 */  
    }  
}
